import {
	IExecuteFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	NodeOperationError,
} from 'n8n-workflow';

import { Function_, App } from 'modal';

export class ModalLabs implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Modal Labs 云平台',
		name: 'modalLabs',
		icon: 'file:modallabs.svg',
		group: ['transform'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: '与 Modal Labs 云平台交互',
		defaults: {
			name: 'Modal Labs 云平台',
			color: '#6366f1',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'modalLabsApi',
				required: true,
			},
		],
		properties: [
			{
				displayName: '资源类型',
				name: 'resource',
				type: 'options',
				noDataExpression: true,
				options: [
					{
						name: '函数',
						value: 'function',
					},
					{
						name: '沙箱',
						value: 'sandbox',
					},
				],
				default: 'function',
			},
			{
				displayName: '操作',
				name: 'operation',
				type: 'options',
				noDataExpression: true,
				displayOptions: {
					show: {
						resource: ['function'],
					},
				},
				options: [
					{
						name: '调用',
						value: 'call',
						description: '调用已部署的 Modal 函数',
						action: '调用函数',
					},
					{
						name: '异步启动',
						value: 'spawn',
						description: '异步启动已部署的 Modal 函数',
						action: '异步启动函数',
					},
				],
				default: 'call',
			},
			{
				displayName: '操作',
				name: 'operation',
				type: 'options',
				noDataExpression: true,
				displayOptions: {
					show: {
						resource: ['sandbox'],
					},
				},
				options: [
					{
						name: '创建',
						value: 'create',
						description: '创建新的沙箱',
						action: '创建沙箱',
					},
					{
						name: '执行',
						value: 'execute',
						description: '在沙箱中执行命令',
						action: '在沙箱中执行命令',
					},
					{
						name: '获取状态',
						value: 'getStatus',
						description: '获取沙箱状态',
						action: '获取沙箱状态',
					},
					{
						name: '终止',
						value: 'terminate',
						description: '终止沙箱',
						action: '终止沙箱',
					},
				],
				default: 'create',
			},
			// Function parameters
			{
				displayName: '应用名称',
				name: 'appName',
				type: 'string',
				required: true,
				displayOptions: {
					show: {
						resource: ['function'],
						operation: ['call', 'spawn'],
					},
				},
				default: '',
				placeholder: 'my-app',
				description: '已部署的 Modal 应用名称',
			},
			{
				displayName: '函数名称',
				name: 'functionName',
				type: 'string',
				required: true,
				displayOptions: {
					show: {
						resource: ['function'],
						operation: ['call', 'spawn'],
					},
				},
				default: '',
				placeholder: 'my-function',
				description: '已部署的 Modal 函数名称',
			},
			{
				displayName: '函数参数',
				name: 'functionArgs',
				type: 'json',
				displayOptions: {
					show: {
						resource: ['function'],
						operation: ['call', 'spawn'],
					},
				},
				default: '{}',
				description: '传递给函数的 JSON 格式参数',
			},
			// Sandbox parameters
			{
				displayName: '应用名称',
				name: 'appName',
				type: 'string',
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['create'],
					},
				},
				default: 'libmodal-example',
				description: 'Modal 应用名称（如果不存在将会创建）',
			},
			{
				displayName: '镜像',
				name: 'image',
				type: 'string',
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['create'],
					},
				},
				default: 'python:3.11',
				description: '沙箱使用的 Docker 镜像',
			},
			{
				displayName: '沙箱 ID',
				name: 'sandboxId',
				type: 'string',
				required: true,
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['execute', 'getStatus', 'terminate'],
					},
				},
				default: '',
				description: '沙箱的 ID',
			},
			{
				displayName: '命令',
				name: 'command',
				type: 'string',
				required: true,
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['execute'],
					},
				},
				default: '',
				placeholder: 'python -c "print(\'Hello World\')"',
				description: '在沙箱中执行的命令',
			},
			{
				displayName: '工作目录',
				name: 'workingDirectory',
				type: 'string',
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['execute'],
					},
				},
				default: '/root',
				description: '命令执行的工作目录',
			},
			{
				displayName: '环境变量',
				name: 'environmentVariables',
				type: 'fixedCollection',
				typeOptions: {
					multipleValues: true,
				},
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['create', 'execute'],
					},
				},
				default: {},
				options: [
					{
						name: 'variable',
						displayName: '变量',
						values: [
							{
								displayName: '名称',
								name: 'name',
								type: 'string',
								default: '',
							},
							{
								displayName: '值',
								name: 'value',
								type: 'string',
								default: '',
							},
						],
					},
				],
			},
			{
				displayName: '超时时间（秒）',
				name: 'timeout',
				type: 'number',
				displayOptions: {
					show: {
						resource: ['sandbox'],
						operation: ['execute'],
					},
				},
				default: 300,
				description: '命令执行的超时时间（秒）',
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];

		const credentials = await this.getCredentials('modalLabsApi');

		// Set Modal credentials as environment variables
		process.env.MODAL_TOKEN_ID = credentials.tokenId as string;
		process.env.MODAL_TOKEN_SECRET = credentials.tokenSecret as string;

		for (let i = 0; i < items.length; i++) {
			try {
				const resource = this.getNodeParameter('resource', i) as string;
				const operation = this.getNodeParameter('operation', i) as string;

				let responseData: any;

				if (resource === 'function') {
					if (operation === 'call') {
						const functionName = this.getNodeParameter('functionName', i) as string;
						const appName = this.getNodeParameter('appName', i) as string;
						const functionArgs = this.getNodeParameter('functionArgs', i, '{}') as string;

						let args: any = {};
						try {
							args = JSON.parse(functionArgs);
						} catch (error) {
							throw new NodeOperationError(this.getNode(), `Invalid JSON in function arguments: ${error}`);
						}

						const func = await Function_.lookup(appName, functionName);
						responseData = await func.remote([], args);

					} else if (operation === 'spawn') {
						const functionName = this.getNodeParameter('functionName', i) as string;
						const appName = this.getNodeParameter('appName', i) as string;
						const functionArgs = this.getNodeParameter('functionArgs', i, '{}') as string;

						let args: any = {};
						try {
							args = JSON.parse(functionArgs);
						} catch (error) {
							throw new NodeOperationError(this.getNode(), `Invalid JSON in function arguments: ${error}`);
						}

						const func = await Function_.lookup(appName, functionName);
						const execution = await func.spawn([], args);
						responseData = {
							execution: execution,
							status: 'spawned',
							appName,
							functionName,
						};
					}
				} else if (resource === 'sandbox') {
					if (operation === 'create') {
						const appName = this.getNodeParameter('appName', i, 'libmodal-example') as string;
						const image = this.getNodeParameter('image', i) as string;
						const environmentVariables = this.getNodeParameter('environmentVariables', i, {}) as any;

						const env: Record<string, string> = {};
						if (environmentVariables.variable) {
							for (const variable of environmentVariables.variable) {
								env[variable.name] = variable.value;
							}
						}

						const app = await App.lookup(appName, { createIfMissing: true });
						const modalImage = await app.imageFromRegistry(image);
						const sandbox = await app.createSandbox(modalImage);

						responseData = {
							sandboxId: sandbox.sandboxId,
							appName,
							image,
						};

					} else if (operation === 'execute') {
						// For sandbox execution, we need to get the sandbox object
						// This is a simplified implementation - in practice you'd need to store sandbox references
						throw new NodeOperationError(this.getNode(), 'Sandbox execution is not yet implemented. Please use the create operation to get a sandbox ID.');

					} else if (operation === 'getStatus') {
						// This would require storing sandbox references
						throw new NodeOperationError(this.getNode(), 'Sandbox status check is not yet implemented.');

					} else if (operation === 'terminate') {
						// This would require storing sandbox references
						throw new NodeOperationError(this.getNode(), 'Sandbox termination is not yet implemented.');
					}
				}

				returnData.push({
					json: responseData,
					pairedItem: {
						item: i,
					},
				});

			} catch (error) {
				if (this.continueOnFail()) {
					returnData.push({
						json: {
							error: error instanceof Error ? error.message : String(error),
						},
						pairedItem: {
							item: i,
						},
					});
					continue;
				}
				throw error;
			}
		}

		return [returnData];
	}
}
